package com.seashop.tree;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.seashop.entity.MemberRelation;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author moredln
 * @desc
 * @date 11/25/20 12:47 AM
 */
@Data
@Slf4j
public class UserNodeRedis {

    private JSONObject jsonObject;
    private int userId = 0;
    /**
     * parentId = -1 when this node has not parent
     */
    private int parentId = 0;
    private int userLevel = 0;
    private String vipExpireTime = "";
    private int promotionLevel = 0;
    private List<Integer> childIds;

    /**
     * statistical counts
     */
    private int[] level2ChildrenCounts;
    private int[] level2TeamCounts;

    private int vipStatus = 0;

    // add new
    public UserNodeRedis(MemberRelation memberRelation) {
        setUserId(memberRelation.getUserId());
        setParentId(memberRelation.getParentId());
        setUserLevel(memberRelation.getUserLevel());
        if (memberRelation.getVipExpireTime() != null) {
            setVipExpireTime(memberRelation.getVipExpireTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        }
        setPromotionLevel(memberRelation.getPromotionLevel());
        setVipStatus(memberRelation.getVipStatus());
        childIds = new ArrayList<>();
        level2ChildrenCounts = new int[4];
        level2TeamCounts = new int[4];
    }

    public UserNodeRedis(String valueJson) {
        this.jsonObject = JSON.parseObject(valueJson);
        setUserId(jsonObject.getInteger("userId"));
        setParentId(jsonObject.getInteger("parentId"));
        setUserLevel(jsonObject.getInteger("userLevel"));
        setVipExpireTime(jsonObject.getString("vipExpireTime"));
        setPromotionLevel(jsonObject.getInteger("promotionLevel"));
        setVipStatus(jsonObject.getInteger("vipStatus"));
        this.childIds = jsonObject.getJSONArray("children").toJavaList(Integer.class);
        this.level2ChildrenCounts = new int[4];
        this.level2TeamCounts = new int[4];
        JSONObject childrenLevelCounts = jsonObject.getJSONObject("childrenLevelCounts");
        Map<String, Integer> res = new HashMap<>(4);
        for (int i = 0; i < 4; i++) {
            String level = String.valueOf(i + 1);
            level2ChildrenCounts[i] = childrenLevelCounts.getIntValue(level);
        }
        JSONObject teamLevelCounts = jsonObject.getJSONObject("teamLevelCounts");
        for (int i = 0; i < 4; i++) {
            String level = String.valueOf(i + 1);
            level2TeamCounts[i] = teamLevelCounts.getIntValue(level);
        }
    }

    public boolean hasChild(Integer childId) {
        return this.childIds.contains(childId);
    }

    public void removeChild(UserNodeRedis childNodeRedis) {
        int childId = childNodeRedis.getUserId();
        int childLevel = childNodeRedis.getUserLevel();
        if (!hasChild(childId)) {
            return;
        }
        this.childIds.remove(childId);
        this.level2ChildrenCounts[childLevel - 1] -= 1;
        this.level2TeamCounts[childLevel - 1] -= 1;
        removeSubTreeTeamCounts(childNodeRedis);
    }

    public void removeDescendantFromAncestorTeam(int descendantLevel) {
        this.level2TeamCounts[descendantLevel - 1] -= 1;
    }

    public void removeSubTreeTeamCounts(UserNodeRedis childNodeRedis) {
        for (int i = 0; i < 4; i++) {
            if (level2TeamCounts[i] < childNodeRedis.getTeamCountByLevel(i + 1)) {
                log.error("Fatal Error: removeSubTreeTeamCounts");
                continue;
            }
            level2TeamCounts[i] -= childNodeRedis.getTeamCountByLevel(i + 1);
        }
    }

    public void addChild(UserNodeRedis childNodeRedis) {
        int childId = childNodeRedis.getUserId();
        int childLevel = childNodeRedis.getUserLevel();
        if (hasChild(childId)) {
            return;
        }
        this.childIds.add(childId);
        this.level2ChildrenCounts[childLevel - 1] += 1;
        this.level2TeamCounts[childLevel - 1] += 1;
        addSubTreeTeamCounts(childNodeRedis);
    }

    public void addDescendant2AncestorTeam(int descendantLevel) {
        this.level2TeamCounts[descendantLevel - 1] += 1;
    }

    public void addSubTreeTeamCounts(UserNodeRedis childNodeRedis) {
        if (!hasChild(childNodeRedis.getUserId())) {
            return;
        }
        for (int i = 0; i < 4; i++) {
            this.level2TeamCounts[i] += childNodeRedis.getTeamCountByLevel(i + 1);
        }
    }

    public void changeTeamCount(int oldLevel, int newLevel) {
        this.level2TeamCounts[oldLevel - 1] -= 1;
        this.level2TeamCounts[newLevel - 1] += 1;
    }

    public void changeChildrenCount(int oldLevel, int newLevel) {
        this.level2ChildrenCounts[oldLevel - 1] -= 1;
        this.level2ChildrenCounts[newLevel - 1] += 1;
    }

    public void degrade() {
        setUserLevel(getUserLevel() - 1);
    }

    public int getTeamCountByLevel(int childLevel) {
        return this.level2TeamCounts[childLevel - 1];
    }

    public int getChildrenCountByLevel(int childLevel) {
        return this.level2ChildrenCounts[childLevel - 1];
    }

}
